﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using CPAIS.Algorithm;
using CPAIS.Model;
using CPAIS.Updater;

namespace CPAIS.Adapter
{
    public class HDPGibbsAdapter
    {
         public struct TopicItem
        {
            public struct Item : IComparable<Item>
            {                
                public string ActionType;
                public double ActionProb;
                public int ActionCount;
                public int CompareTo(Item other)
                {
                    if (this.ActionProb > other.ActionProb)
                        return -1;
                    else if (this.ActionProb < other.ActionProb)
                        return 1;
                    else
                        return 0;
                }
            }

            public string name;
            public double Prob;
            public Item[] Actions;
        }

        private readonly List<Trace> traces;
        private int M, V, K;
        private double alpha, beta;
        private int iterations;
        private string[] vocab;
        private int[][] docs;
        private Dictionary<string, double[]> docTopicAssoc;
        private TopicItem[] topicWordAssoc;
        private double perplexity;
        private double klDivergence;
        private double entropy;
        //bool timeConsider = true;
        public HDPGibbsAdapter(LogModel logModel)
        {           
            traces = logModel.Traces;
            /*
            if (!timeConsider)
            {

                vocab = logModel.ActionTypes;
                M = traces.Count;
                V = vocab.Length;

                docs = new int[M][];
                for (int m = 0; m < M; m++)
                {
                    var actions = traces[m].Actions;
                    int N = actions.Count;
                    docs[m] = new int[N];
                    for (int n = 0; n < N; n++)
                    {
                        docs[m][n] = actions[n].TypeIndex;
                    }
                }
            }
                
            else
            {
                List<string> timeStampActionList = new List<string>();
                foreach (Trace trace in traces)
                {
                    foreach (ActionEvent act in trace.Actions)
                    {
                        string szAct = act.TypeName + ":" + act.Day.ToString();
                        if (!timeStampActionList.Contains(szAct))
                            timeStampActionList.Add(szAct);
                    }
                }
                vocab = timeStampActionList.ToArray();
                M = traces.Count;
                V = vocab.Length;

                docs = new int[M][];
                for (int m = 0; m < M; m++)
                {
                    var actions = traces[m].Actions;
                    int N = actions.Count;
                    docs[m] = new int[N];
                    for (int n = 0; n < N; n++)
                    {
                        string szAct = actions[n].TypeName + ":" + actions[n].Day.ToString();
                        int index = timeStampActionList.IndexOf(szAct);
                        docs[m][n] = index;
                    }
                }
            }*/
        }

        public int VocabularySize { get { return V; } }
        public int DocumentSize { get { return M; } }
        public int TopicSize { get { return K; } }
        public double Alpha { get { return alpha; } }
        public double Beta { get { return beta; } }
        public int Iterations { get { return iterations; } }

        public Dictionary<string, double[]> DocTopicAssoc
        {
            get { return docTopicAssoc; }
        }
        public TopicItem[] TopicWordAssoc
        {
            get { return topicWordAssoc; }
        }
        public double Perplexity { get { return perplexity; } }
        public double KLDivergence { get { return klDivergence; } }
        public double Entropy { get { return entropy; } }

        public void Execute(int iterations, ProgressUpdater updater)
        {
            // Records parameters          
            this.iterations = iterations;

            List<string> timeStampActionList = new List<string>();
            foreach (Trace trace in traces)
            {
                foreach (ActionEvent act in trace.Actions)
                {
                    string szAct = act.TypeName + ":" + act.Day.ToString();
                    if (!timeStampActionList.Contains(szAct))
                        timeStampActionList.Add(szAct);
                }
            }
            vocab = timeStampActionList.ToArray();
            M = traces.Count;
            V = vocab.Length;

            docs = new int[M][];
            for (int m = 0; m < M; m++)
            {
                var actions = traces[m].Actions;
                int N = actions.Count;
                docs[m] = new int[N];
                for (int n = 0; n < N; n++)
                {
                    string szAct = actions[n].TypeName + ":" + actions[n].Day.ToString();
                    int index = timeStampActionList.IndexOf(szAct);
                    docs[m][n] = index;
                }
            }
            // Gibbs sampling
            HDPGibbs  hdp = new HDPGibbs();
            hdp.addInstances(docs, V);

            
	        Console.WriteLine("sizeOfVocabulary = "+hdp.sizeOfVocabulary);
		    Console.WriteLine("totalNumberOfWords = "+hdp.totalNumberOfWords);
		    Console.WriteLine("NumberOfDocs = "+hdp.docStates.Length);

            try
            {
                if (updater != null)
                    updater.Begin();

                hdp.run(0, iterations, updater);

                K = hdp.numberOfTopics;
                /*
                for (int k = 0; k < hdp.numberOfTopics; k++)
                {
                    for (int w = 0; w < hdp.sizeOfVocabulary; w++)
                        Console.WriteLine( hdp.wordCountByTopicAndTerm[k][w]);
                  
                }*/

                // Gets Document-Topic association
                docTopicAssoc = new Dictionary<string, double[]>();
                for (int m = 0; m < M; m++)
                {
                    HDPGibbs.DOCState doc = hdp.getInstance(m);
                    double[] probs = new double[hdp.numberOfTopics];
                    double dTotal = 0.0;
                    for (int i = 0; i < doc.documentLength; i++)
                    {
                        int table = doc.words[i].tableAssignment;
                        int topic = doc.tableToTopic[table];
                        probs[topic]++;
                        dTotal++;
                    }

                  
                    for (int k = 0; k < hdp.numberOfTopics; k++)
                    {
                        probs[k] /= dTotal;
                    }
                    docTopicAssoc.Add(traces[m].ID, probs);
                }

                // Gets Topic-Word association

                topicWordAssoc = new TopicItem[hdp.numberOfTopics];
                for (int k = 0; k < hdp.numberOfTopics; k++)
                {
                    topicWordAssoc[k].name = "Pattern: " + Convert.ToString(k);
                    topicWordAssoc[k].Actions = new TopicItem.Item[V];
                    for (int v = 0; v < V; v++)
                    {
                        topicWordAssoc[k].Actions[v].ActionType = vocab[v];
                        topicWordAssoc[k].Actions[v].ActionProb = ((double)hdp.wordCountByTopicAndTerm[k][v]) / (double)hdp.totalNumberOfWords;
                        topicWordAssoc[k].Actions[v].ActionCount = hdp.wordCountByTopicAndTerm[k][v];
                    }
                    topicWordAssoc[k].Prob = 0;
                    foreach (var key in docTopicAssoc.Keys)
                    {
                        topicWordAssoc[k].Prob += docTopicAssoc[key][k];
                    }
                    topicWordAssoc[k].Prob /= M;
                    Array.Sort(topicWordAssoc[k].Actions);
                }


                /*int t, docID;
                for (int d = 0; d < hdp.docStates.Length; d++)
                {
                    Algorithm.HDPGibbs.DOCState docState = hdp.docStates[d];
                    docID = docState.docID;
                    for (int i = 0; i < docState.documentLength; i++)
                    {
                        t = docState.words[i].tableAssignment;
                        Console.WriteLine(docID + " " + docState.words[i].termIndex + " " + docState.tableToTopic[t] + " " + t);
                    }
                }*/
         

                if (updater != null)
                {
                    updater.Complete();
                }
            }
            catch (Exception ex)
            {
                if (updater != null)
                {
                    if (updater.State == UpdateState.Cancelled)
                        return;
                    else
                        updater.Fail(ex.Message);
                }
                else
                    throw ex;

                return;
            }
        }
    }
}
